# 命名SQL开发指南
# 基础规范
命名SQL的ID全局唯一,且ID中不允许包含英文句点(.);
XML文件头部必须保留固定声明,不可修改/删除:
包含特殊字符(如</>/&)的SQL需包裹在
<![CDATA[]]>中;
# 基本增删改查示例
# 新增(INSERT)
场景:向产品表(PRODUCT)插入一条数据,入参为实体类 / Map,支持 NULL 值替换。
<!-- 方式1:使用parameterClass + 内联参数(推荐) -->
<insert id="insertProduct" parameterClass="com.primeton.model.data.Product">
INSERT INTO PRODUCT (
ID,
CODE,
DESCRIPTION,
SUGGESTED_PRICE
) VALUES (
#id#,
#code#,
#description:VARCHAR:NO_ENTRY#, <!-- NULL值替换:description为NO_ENTRY时存入NULL -->
#suggestedPrice:DECIMAL#
)
</insert>
<!-- 方式2:使用parameterMap(兼容场景) -->
<parameterMap id="insertProductParam" class="com.primeton.model.data.Product">
<parameter property="id" javaType="int" nullValue="-999999"/>
<parameter property="code" javaType="string"/>
<parameter property="description" javaType="string" nullValue="NO_ENTRY"/>
<parameter property="suggestedPrice" javaType="DECIMAL" nullValue="0.00"/>
</parameterMap>
<insert id="insertProductByParamMap" parameterMap="insertProductParam">
INSERT INTO PRODUCT (ID, CODE, DESCRIPTION, SUGGESTED_PRICE)
VALUES (?, ?, ?, ?)
</insert>
# 查询(SELECT)
场景:根据产品 ID 查询单条数据,入参为Integer,结果映射为实体类。
<!-- 结果映射:自定义ResultMap -->
<resultMap id="productResultMap" class="com.primeton.model.data.Product">
<result property="id" column="ID" jdbcType="NUMERIC" />
<result property="code" column="CODE" jdbcType="VARCHAR"/>
<result property="description" column="DESCRIPTION" jdbcType="VARCHAR"/>
<result property="suggestedPrice" column="SUGGESTED_PRICE" jdbcType="DECIMAL"/>
</resultMap>
<select id="selectProductById" parameterClass="java.lang.Integer" resultMap="productResultMap">
<![CDATA[
SELECT ID, CODE, DESCRIPTION, SUGGESTED_PRICE
FROM PRODUCT
WHERE ID = #value#
]]>
</select>
<!-- 别名简化:int等价于java.lang.Integer -->
<select id="selectProductByIntId" parameterClass="int" resultClass="com.primeton.model.data.Product">
SELECT ID as id, CODE as code FROM PRODUCT WHERE ID = #value#
</select>
场景:查询产品列表,结果自动映射(resultClass)。
<select id="selectAllProduct" resultClass="com.primeton.model.data.Product">
SELECT
ID as id, <!-- 别名匹配实体属性名 -->
CODE as code,
DESCRIPTION as description,
SUGGESTED_PRICE as suggestedPrice
FROM PRODUCT
ORDER BY ID
</select>
场景:多条件查询产品,入参为HashMap(包含分类ID、产品编码)。
<select id="selectProductByMap" parameterClass="java.util.Map" resultMap="productResultMap">
<![CDATA[
SELECT ID, CODE, DESCRIPTION
FROM PRODUCT
WHERE CAT_ID = #catId# AND CODE = #code#
]]>
</select>
场景:按产品名称模糊查询,入参为Product实体类。
<select id="selectProductByDescription" parameterClass="com.primeton.model.data.Product" resultMap="productResultMap">
<![CDATA[
SELECT ID, CODE, DESCRIPTION
FROM PRODUCT
WHERE DESCRIPTION LIKE '%$description$%' <!-- $参数$:直接拼接SQL(适用于like/in) -->
]]>
</select>
场景:查询产品总数,结果映射为Integer。
<!-- resultClass自动映射(简化) -->
<select id="selectProductCountSimplify" resultClass="int">
SELECT COUNT(1) as value FROM PRODUCT
</select>
# 更新(UPDATE)
场景:根据产品 ID 更新名称和价格,入参为 Map。
<update id="updateProduct" parameterClass="java.util.Map">
<![CDATA[
UPDATE PRODUCT
SET CODE = #code#, SUGGESTED_PRICE = #price#
WHERE ID = #id#
]]>
</update>
# 删除(DELETE)
场景:根据产品 ID 删除数据,入参为基本类型。
<delete id="deleteProductById" parameterClass="int">
DELETE FROM PRODUCT WHERE ID = #value#
</delete>
# 基础条件动态SQL
动态 SQL 通过条件标签(如
场景:多条件查询账户(ACCOUNT),支持姓名、邮箱、ID的动态过滤。
<select id="dynamicSelectAccount" parameterClass="com.primeton.model.data.Account" resultMap="accountResultMap">
SELECT * FROM ACCOUNT
<dynamic prepend="WHERE">
<!-- 姓名非空时:按姓名/ lastName模糊查询 -->
<isNotNull prepend="AND" property="firstName">
(ACC_FIRST_NAME = #firstName#
<isNotNull prepend="OR" property="lastName">
ACC_LAST_NAME = #lastName#
</isNotNull>)
</isNotNull>
<!-- 邮箱非空时:模糊匹配 -->
<isNotNull prepend="AND" property="emailAddress">
ACC_EMAIL LIKE #emailAddress#
</isNotNull>
<!-- ID大于0时:精确匹配 -->
<isGreaterThan prepend="AND" property="id" compareValue="0">
ACC_ID = #id#
</isGreaterThan>
</dynamic>
ORDER BY ACC_LAST_NAME
</select>
# 存储过程调用示例
场景:调用存储过程swap_email_address,交换两个邮箱地址(INOUT参数)。
<!-- 定义参数映射:Map入参,INOUT模式 -->
<parameterMap id="swapEmailParamMap" class="java.util.Map">
<parameter property="email1" jdbcType="VARCHAR" javaType="java.lang.String" mode="INOUT"/>
<parameter property="email2" jdbcType="VARCHAR" javaType="java.lang.String" mode="INOUT"/>
</parameterMap>
<!-- 调用存储过程 -->
<procedure id="callSwapEmail" parameterMap="swapEmailParamMap">
{call swap_email_address (?, ?)} <!-- 标准JDBC存储过程语法 -->
</procedure>
# SQL片段复用与复杂条件查询示例
本示例基于PRODUCT表结构,演示SQL片段定义、复用(include标签)、in条件循环(iterate)及多条件动态查询,覆盖常用复杂查询场景。
# 定义ResultMap与SQL片段
先定义通用ResultMap和查询字段片段,供后续查询语句复用,减少重复代码。
<!-- 定义PRODUCT表通用ResultMap -->
<resultMap class="com.primeton.model.data.Product" id="productCommonResultMap">
<result column="ID" property="id" />
<result column="CODE" property="code" />
<result column="DESCRIPTION" property="description" />
<result column="SUGGESTED_PRICE" property="suggestedPrice" />
<result column="CREATE_TIME" property="createTime" />
<result column="UPDATE_TIME" property="updateTime" />
</resultMap>
<!-- 定义PRODUCT表通用查询字段片段,通过id复用 -->
<sql id="selectProductCommonVo">
select d.ID, d.CODE, d.DESCRIPTION, d.SUGGESTED_PRICE,
d.CREATE_TIME, d.UPDATE_TIME
from PRODUCT d
</sql>
# 复用查询
<!-- 根据分类ID查询产品列表,结果自动映射(resultMap) -->
<select id="selectProductById" parameterClass="int" resultMap="productCommonResultMap">
<include refid="selectProductCommonVo" />
where d.CAT_ID = #value#
</select>
<!--按实体多条件动态查询-->
<select id="selectProductByEntity" parameterClass="com.primeton.model.data.Product" resultMap="productCommonResultMap">
<include refid="selectProductCommonVo"/>
where d.CAT_ID = #catId#
<isNotEmpty property="code">
and d.CODE like '%$code$%' <!-- 产品编码模糊查询 -->
</isNotEmpty>
</select>
<!-- 定义ACCOUNT表通用查询片段 -->
<sql id="selectAccountCommonVo">
select a.ACC_ID, a.ACC_FIRST_NAME, a.ACC_LAST_NAME, a.ACC_EMAIL
from ACCOUNT a
</sql>
<!-- 关联查询:查询指定账户关联的产品列表 -->
<select id="selectProductByAccountId" parameterClass="java.lang.Long" resultMap="productCommonResultMap">
<include refid="selectProductCommonVo"/>
inner join ACCOUNT a on d.ACC_ID = a.ACC_ID <!-- PRODUCT表通过ACC_ID关联ACCOUNT表 -->
<include refid="selectAccountCommonVo"/>
where a.ACC_ID = #accountId#
</select>
# 注意事项
- 参数符号区分:
#property#:生成PreparedStatement,参数预编译(推荐,防SQL注入);$property$:直接拼接SQL(仅适用于like、in等场景,需自行处理注入风险)。
- NULL值处理:
- 入参:通过#property:jdbcType:nullValue#或parameterMap的nullValue属性替换NULL;
- 出参:通过resultMap的nullValue属性将数据库NULL映射为指定值。
- 多数据库适配:
- 方式1:ID后缀匹配(如selectProduct.mysql_5.7);
- 方式2:动态SQL中使用context.databaseType/context.databaseVersion。
特殊字符处理:包含</>/&等字符的SQL需包裹在中。
参数类型映射:
- 基本类型只能有一个属性,名字必须用"value";
- 使用Map作为参数时,key值对应SQL中的参数名;
- 内联参数支持jdbcType和nullValue的指定。
- 动态SQL标签:
- 二元条件元素:isEqual、isNotEqual、isGreaterThan、isGreaterEqual、isLessThan、isLessEqual
- 一元条件元素:isNull、isNotNull、isNotEmpty